//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Core.h".
//-------------------------------------------------------------------

#ifndef _UXS_FRACTIONAL_H
#define _UXS_FRACTIONAL_H

#pragma once

#include "TemplateMath.h"
#include "TTypeInfo.h"
#include "../Containers/TString.h"
#include "TStaticFloat.h"

namespace UX_STL
{
namespace CompileTime
{

	//
	// Fractional
	//

	template <typename T, T Numerator, T Denominator = 1>
	struct TFractional
	{
	// checks
		STATIC_ASSERT( Denominator > 0, "denominator must be positive no-zero value!" );


	// types
	private:
		static const T	_GCD = Abs< T, GreatestCommonDivisor< T, Numerator, Denominator >::value >::value;
		STATIC_ASSERTE( _GCD > 0 );

	public:
		static const T	N	= Numerator / _GCD;
		static const T	D	= Denominator / _GCD;
		
		typedef T													value_t;
		typedef typename NearFloat::FromSize< sizeof(T)*2 >::type	float_t;
		typedef TFractional< T, Numerator, Denominator >			Self;
		typedef TFractional< T, N, D >								Simplified;


	// static values
	public:
		template <typename Frac>
		struct Add {
			typedef typename TFractional< T, N * Frac::D + Frac::N * D, D * Frac::D >::Simplified	type;
		};
		
		template <typename Frac>
		struct Sub {
			typedef typename TFractional< T, N * Frac::D - Frac::N * D, D * Frac::D >::Simplified	type;
		};

		struct Neg {
			typedef typename TFractional< T, 0-N, D >::Simplified	type;
		};
		
		template <typename Frac>
		struct Mul {
			typedef typename TFractional< T, N * Frac::N, D * Frac::D >::Simplified	type;
		};
		
		template <typename Frac>
		struct Div {
			typedef typename TFractional< T, N * Frac::D, D * Frac::N >::Simplified	type;
		};

		template <uint ToPower>
		struct Pow {
			typedef typename TFractional< T, CompileTime::Pow< T, N, ToPower >::value, CompileTime::Pow< T, D, ToPower >::value >::Simplified	type;
		};

		struct IsZero {
			static const bool	value = (N == 0);
		};

		struct IsInteger {
			static const bool	value = (D == 1);
		};

		struct Round {
			static const T		rounding = ((Abs< T, N >::value * 10) / D) % 10 >= 5;
			static const T		value	 = (N / D) + (rounding * Sign< T, N >::value);
		};

		template <typename Frac>
		struct Equal {
			static const bool	value = (N == 0 and Frac::N == 0) or (N == Frac::N and D == Frac::D);
		};

		template <T Value>
		struct EqualInt {
			static const bool	value = (N == Value and D == 1);
		};

		template <typename MT, typename ET>
		struct ToStaticFloat {
			typedef TStaticFloat< MT, ET, N >	n;
			typedef TStaticFloat< MT, ET, D >	d;
			typedef typename n::Div< d >::type	type;
		};

		template <typename StaticFloatType>
		struct ToStaticFloatType {
			typedef typename ToStaticFloat< typename StaticFloatType::mantissa_t, typename StaticFloatType::exponent_t >::type	type;
		};

		template <typename B>
		struct To {
			typedef TFractional< B, N, D >		type;
		};


	// static methods
	public:
		template <typename FT>
		static FT ToFloat ()
		{
			return FT( N ) / FT( D );
		}

		static string ToString ()
		{
			string	s;
			s << string().FormatI( N, 10 );

			if ( N != 0 and D > 1 )
				s << '/' << string().FormatI( D, 10 );
			
			return s;
		}

	// methods
	public:
		operator float_t () const
		{
			return ToFloat< float_t >();
		}
	};


	template <int16 Numerator, int16 Denominator = 1>	struct Fractional16 { typedef TFractional< int16, Numerator, Denominator >	type; };
	template <int32 Numerator, int32 Denominator = 1>	struct Fractional32 { typedef TFractional< int32, Numerator, Denominator >	type; };
	template <int64 Numerator, int64 Denominator = 1>	struct Fractional64 { typedef TFractional< int64, Numerator, Denominator >	type; };


}	// CompileTime
}	// UX_STL

#endif	// _UXS_FRACTIONAL_H